卡顿优化方法论

卡顿优化

卡顿优化方法论

卡顿优化,也就是提升 APP 的响应时间,用空间换时间,总结了四项基本原则:

  1. 缓存优先,有缓存读缓存
  2. 减少新建,能复用就不新建
  3. 减少任务,能不做的尽量不做
  4. 具体问题具体分析:针对具体事务本身进行分析,必须做的能提前做就提前做,不必须的延后做

优化措施

任务执行

  1. 任务优先级排序,优先高优先级的执行
    1. 线程设置 Priority
    2. 线程池按优先级加入线程池,最好用阻塞式:PriorityBlockingQueue,可参考 XTask
  2. 将一些不重要的任务延迟执行
    1. 延迟几秒或几分钟执行
    2. 不必要的任务不执行,需要用到的时候再去执行
    3. 空闲执行,Idlehandler

资源加载

  1. 懒加载
  2. 分段加载(部分加载)
  3. 预加载(数据、布局页面等)

懒加载

分段加载

  1. 大图的分段加载
  2. 长视频分段加载
  3. 大文件或长 WebView 分段加载

预加载

数据结构

数据结构优化

1.ArrayList 和 LinkedList:

2.HashMap 和 SparseArray:

3.Set: 保证每个元素都必须是唯一的。

4.TreeSet 和 TreeMap:有序的集合,保证存放的元素是排过序的,速度慢于 HashSet 和 HashMap。

数据缓存

对于一些变化不是很频繁的数据资源,我们可以将其缓存下来。这样我们下次需要使用它们的时候,就可以直接读取缓存,这样极大地减少了加载和渲染所需要的时间。

一般意义上的缓存,按读取的时间由快到慢,我们可分为内存缓存、磁盘缓存、网络缓存。

某种意义上来说,内存缓存、磁盘缓存和网络缓存,它们又是可以相互转化的,一般来说,我们会将 网络缓存->磁盘缓存->内存缓存,进行使用,从而提升读取速度。

具体我们可以参考 glide框架RecyclerView 的实现原理。

锁优化

锁是我们解决并发的重要手段,但是如果滥用锁的话,很可能造成执行效率下降,更严重的可能造成死锁等无法挽回的场景。

当我们需要处理高并发的场景时,同步调用尤其需要考量锁的性能损耗:

具体做法:

  1. 使用乐观锁代替悲观锁,轻量级锁代替重量级锁。
  2. 缩小同步范围,避免直接使用 synchronized,即使使用也要尽量使用同步块而不是同步方法。多使用 JDK 提供给我们的同步工具:CountDownLatchCyclicBarrierConcurrentHashMap
  3. 针对不同使用场景,使用不同类型的锁。
    • 针对并发读多,写少的,我们可以使用读写锁(多个读锁不互斥,读锁与写锁互斥):ReentrantReadWriteLockCopyOnWriteArrayListCopyOnWriteArraySet
    • 针对某一个并发操作通常由某一特定线程执行时,可尝试使用偏向锁(偏向于第一个获得它的线程)。
    • 针对存在大量并发资源竞争的场景,推荐使用重量级锁 synchronized Java线程安全-锁#synchronized

IO 优化

  1. 线程优化(统一、优先级调度、任务特性)

  2. IO 优化(网络 IO 和磁盘 IO),核心是减少 IO 次数

    • 网络:请求合并,请求链路优化,请求体优化,系列化和反序列化优化,请求复用等。
    • 磁盘:文件随机读写、SharePreference 读写等(例如对于读多写少的,可使用内存缓存)
  3. log 优化(循环中的 log 打印,不必要的 log 打印,log 等级)

  4. 异步 IO OkIO 库

线程优化

  1. 使用线程池
  2. 统一应用内的线程池,避免一个业务一个线程池
  1. 使用 Hook 的方式,收集应用内所以使用 newThread 方法的地方,改为由线程池管理者统一协调管理。
    • 主线程池:核心线程数和最大线程数:2n(n 为 CPU 核心数),60s keepTime,PriorityBlockingQueue(128)。
    • 副线程池:核心线程数和最大线程数:n(n 为 CPU 核心数),60s keepTime,PriorityBlockingQueue(64)。
    • 线程池做成后端动态可配置
  2. 将所有提供了设置线程池接口的第三方库,通过其开放的接口,设置为线程池管理者管理。没有提供设置接口的,考虑替换库或者插桩的方式,替换线程池的使用。

IO 优化

IO 优化的核心是减少 IO 次数。

  1. 网络请求优化。
    • 避免不必要的网络请求。对于那些非必要执行的网络请求,可以延时请求或者使用缓存。
    • 对于需要进行多次串行网络请求的接口进行优化整合,控制好请求接口的粒度。比如后台有获取用户信息的接口、获取用户推荐信息的接口、获取用户账户信息的接口。这三个接口都是必要的接口,且存在先后关系。如果依次进行三次请求,那么时间基本上都花在网络传输上,尤其是在网络不稳定的情况下耗时尤为明显。但如果将这三个接口整合为获取用户的启动(初始化)信息,这样数据在网络中传输的时间就会大大节省,同时也能提高接口的稳定性。
  2. 磁盘 IO 优化
    • 避免不必要的磁盘 IO 操作。这里的磁盘 IO 包括:文件读写、数据库(sqlite)读写和 SharePreference 等。
    • 对于数据加载,选择合适的数据结构。可以选择支持随机读写、延时解析的数据存储结构以替代 SharePreference。
    • 避免程序执行出现大量的序列化和反序列化(会造成大量的对象创建)。

布局

UI优化

内存优化

内存优化基础

卡顿优化分析

muywy

卡顿优化实践

mmkv 替换掉 sp

ANR free implementation of SharedPreferences.

RNoMainThreadWriteSharedPreferencesecyclerView 的优化

  1. 支持 DiffUtils(itemChange, payloads)
  2. RecyclerViewPool
  3. 其他?